/* linux-sio.c --- communication with gsm module via serial port */
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <fcntl.h>
#include <termios.h>
#include <pthread.h>
  
#define _POSIX_SOURCE 1 /* POSIX compliant source */

#define MUX_MAX_COM		3

typedef struct _MuxContext
{
	int				fd_num;
	int				fds[MUX_MAX_COM];
	fd_set			read_fdset;
	struct termios	oldtio;
	struct termios	newtio;
} MuxContext;

typedef struct _MuxContext MuxConnection;

#define TIO_BAUDRATE_DEFAULT B115200

#define	SERIAL_TTY_NAME_LEN	80
//#define SERIAL_TTY_NAME "/dev/ttyUSB0"
#define SERIAL_TTY_NAME "/dev/ttyS0"
#define MUX_PORT_PREFIX "/dev/mux"

typedef struct _BaudTioBaudMap
{
	unsigned int	baudrate;
	unsigned int	tio_baudrate;
} BaudTioBaudMap;

static const BaudTioBaudMap tio_baudrate_map_array[] =
{
	50,			B50,
	75,			B75,	
	110,		B110,
	134,		B134,
	150,		B150,
	200,		B200,
	300,		B300,
	600,		B600,
	1200,		B1200,
	1800,		B1800,
	2400,		B2400,
	4800,		B4800,
	9600,		B9600,
	19200,		B19200,
	38400,		B38400,
	57600,		B57600,
	115200,		B115200,
	230400,		B230400,
	460800,		B460800,
	500000,		B500000,
	576000,		B576000,
	921600,		B921600,
	1000000,	B1000000,
	1152000,	B1152000,
	1500000,	B1500000,
	2000000,	B2000000,
	2500000,	B2500000,
	3000000,	B3000000,
	3500000,	B3500000,
	4000000,	B4000000
};


static unsigned int GetTioBaudrate (unsigned int baudrate)
{
	int i;

	for (i=0; i<sizeof(tio_baudrate_map_array)/sizeof(tio_baudrate_map_array[0]); i++)
	{
		if (tio_baudrate_map_array[i].baudrate == baudrate)
			return tio_baudrate_map_array[i].tio_baudrate;
	}

	return TIO_BAUDRATE_DEFAULT;
}

#define	CONFIG_LINE_LEN			80
static int open_serial_port (const char* serial_tio_name, MuxContext* mux_context, unsigned int tio_baudrate)
{
    int fd;
    struct termios* newtio_p;

	fd = open (serial_tio_name, O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd <0)
    {   
        perror(serial_tio_name);
        return 0;
    }

    tcgetattr(fd, &mux_context->oldtio); /* save current port settings */

    newtio_p = &mux_context->newtio;
    bzero(newtio_p, sizeof(*newtio_p));

	//fcntl(fd, F_SETFL, 0);
	newtio_p->c_cflag = CLOCAL | CREAD | CS8 | HUPCL | tio_baudrate | CRTSCTS;
	newtio_p->c_cflag &= ~tio_baudrate;
	newtio_p->c_cflag |= tio_baudrate;

	newtio_p->c_iflag = IGNBRK;	
    newtio_p->c_iflag &= ~(INLCR | ICRNL | IGNCR);;
    /* set input mode */
    newtio_p->c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

	// set raw output
	newtio_p->c_oflag &= ~OPOST;
	newtio_p->c_oflag &= ~OLCUC;
	newtio_p->c_oflag &= ~ONLRET;
	newtio_p->c_oflag &= ~ONOCR;
	newtio_p->c_oflag &= ~OCRNL;

    tcflush(fd, TCIFLUSH);
    tcsetattr(fd, TCSANOW, newtio_p);

    mux_context->fd_num = 1;
    mux_context->fds[0] = fd;
    FD_SET (fd, &mux_context->read_fdset);

    return 1;
}

static int open_pty (const char* serial_tio_name, MuxContext* mux_context)
{
    int fd;
    struct termios* newtio_p;
    
    fd = open(serial_tio_name, O_RDWR | O_NONBLOCK);
    if (fd <0)
    {
        perror(serial_tio_name);
        return 0;
    }

    tcgetattr(fd, &mux_context->oldtio); /* save current port settings */
    newtio_p = &mux_context->newtio;
    // get the parameters
    bzero(newtio_p, sizeof(*newtio_p));
    tcgetattr(fd, newtio_p);
    // set raw input
    newtio_p->c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    newtio_p->c_iflag &= ~(INLCR | ICRNL | IGNCR);

    // set raw output
    newtio_p->c_oflag &= ~OPOST;
    newtio_p->c_oflag &= ~OLCUC;
    newtio_p->c_oflag &= ~ONLRET;
    newtio_p->c_oflag &= ~ONOCR;
    newtio_p->c_oflag &= ~OCRNL;
    tcsetattr(fd, TCSANOW, newtio_p);

    mux_context->fd_num = 1;
    mux_context->fds[0] = fd;
    FD_SET (fd, &mux_context->read_fdset);

    return 1;
}

MuxConnection* mux_open (int com, const char* serial_tio_name, unsigned int    tio_baudrate)
{
	int fd;
    int open_result = 0;
	MuxConnection*  mux_connection;

	mux_connection = (MuxConnection*) calloc (1, sizeof (MuxConnection));
	if (mux_connection == NULL)
	{
		perror ("Can't new mux_connection.\n");
		return NULL;
	}

    if (strncmp (serial_tio_name, MUX_PORT_PREFIX, strlen (MUX_PORT_PREFIX)) == 0)
    {
        open_result = open_pty (serial_tio_name, mux_connection);
    }
    else
    {
        open_result = open_serial_port (serial_tio_name, mux_connection, GetTioBaudrate (tio_baudrate));
    }

    if (!open_result)
    {
        free (mux_connection);
        mux_connection = NULL;
    }
	
	return mux_connection;
}

int mux_close (MuxConnection* connection)
{
	MuxContext* mux_context;

	mux_context = (MuxContext*) connection;
	tcsetattr(mux_context->fds[0], TCSANOW, &mux_context->oldtio);
	close (mux_context->fds[0]);

	return 1;
}

int mux_read (MuxConnection* connection, unsigned char* buf, unsigned long buf_size)
{
	int ret = 0;
	MuxContext* mux_context;

	mux_context = (MuxContext*) connection;
	ret = read (mux_context->fds[0], buf, buf_size);

	return ret;
}

int mux_write (MuxConnection* connection, const unsigned char* buf, unsigned long buf_size)
{
	MuxContext* mux_context;

	mux_context = (MuxContext*) connection;
	return write (mux_context->fds[0], buf, buf_size);
}

int mux_ioctl (MuxConnection* connection, unsigned int cmd, unsigned long arg)
{
	MuxContext* mux_context;

	mux_context = (MuxContext*) connection;
	return ioctl (mux_context->fds[0], cmd, arg);
}

int mux_select (MuxConnection* connection, long timeout)
{
	int    i;
	int    ret;
	int    max_fd;
	struct timeval waittime;
	MuxContext* mux_context;

	mux_context = (MuxContext*) connection;

	max_fd = 0;
	FD_ZERO(&mux_context->read_fdset);
	for (i=0; i<mux_context->fd_num; i++)
	{
		FD_SET (mux_context->fds[i], &mux_context->read_fdset);
		if (mux_context->fds[i] > max_fd)
		{
			max_fd = mux_context->fds[i];
		}
	}
	max_fd += 1;
	if (timeout >= 0)
	{
		waittime.tv_usec = (timeout % 1000) * 1000;
		waittime.tv_sec  = timeout / 1000;
		ret = select (max_fd, &mux_context->read_fdset, NULL, NULL, &waittime);
	}
	else
	{
		ret = select (max_fd, &mux_context->read_fdset, NULL, NULL, NULL);
	}

	if (ret > 0)
	{
		ret = 0x02;
	}
		
	return ret;
}

int mux_poll (MuxConnection* connection, unsigned long timeout)
{
	MuxContext* mux_context;

	mux_context = (MuxContext*) connection;
	return 0;
}

unsigned long mux_get_com_fd (MuxConnection* connection)
{
	return connection->fds[0];
}
   
#define CMDLINE_LEN		1023
int main1 (int argc, char** argv)
{
	int count = atoi (argv[2]);
	int write = 0;
	MuxConnection* connection;

	connection = mux_open (1, argv[1], 115200);
	if (connection == NULL)
	{
		return -1;
	}

	while (write < count)
	{
		write += mux_write (connection, "123456789", strlen ("123456789"));
		printf ("wrote =%d \n", write);
	}

	printf ("count = %d\n", count);

	return 0;
}


int get_cur_time ()
{
	int ret = 0;
	struct timeval tv = {0};
	struct timezone tz = {0};

	gettimeofday (&tv, &tz);

	ret = tv.tv_sec * 1000 + tv.tv_usec / 1000;

	return ret;
}


#include "modemcontrol.h"
#define MTK_MODULE_DEVICE_NAME "/system/modem"

static void read_mainLoop(void* data)
{
	int ret = 0;
	int nbytes = 0;
	char buf[CMDLINE_LEN+1];
	MuxConnection* connection = (MuxConnection*)data;

	while(1)
	{
		if(mux_select (connection, 0) > 0) {
			nbytes = mux_read (connection, buf, CMDLINE_LEN);
			buf[nbytes] = '\0';
			printf("%s\n", buf);
		}
	}
}

int main(int argc, char **argv)
{
	int ret;
	int nbytes;
	struct termios oldtio,newtio;
	MuxConnection* connection;
	char buf[CMDLINE_LEN+1];
	int repeat_times = 10;
	int i = 0;
	int fd = 0;
	pthread_attr_t attr;
	pthread_t tid;

	
	if (argc == 1)
	{
		connection = mux_open (1, SERIAL_TTY_NAME, 115200);
	}
	else
	{
		connection = mux_open (1, argv[1], 115200);
	}
	if (connection == NULL)
	{
		return -1;
	}
	
	pthread_attr_init (&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    pthread_create(&tid, &attr, read_mainLoop, connection);

	while (1)
	{
		size_t len;
		int start  = 0;
		int end = 0;

	
		/* get AT command from stdin */
		
		fgets (buf, CMDLINE_LEN-1, stdin);
		len = strlen (buf);
		if(len < 2) {
			continue;
		}
		buf[len] = buf[len-1];
		buf[len-1] = '\r';
		buf[len+1] = '\0';
		if (strcmp (buf, "quit\r\n") == 0)
		{
			break;
		}
		mux_write (connection, buf, strlen (buf));
	}

	mux_close (connection);
	return 0;
}



